// A few extra functions for fsi

[<AutoOpen>]
module Extra

open System.IO
open System.Reflection
open Microsoft.FSharp.Reflection

// Dynamic lookup operator, for duck typing
// e.g. obj?ToUpper()
let (?) x m args =
    let args =
        if box args = null then (* the unit value is null, once boxed *)
            [||]
        elif FSharpType.IsTuple (args.GetType()) then
            FSharpValue.GetTupleFields args
        else
            [|args|]
    x.GetType().InvokeMember(m, BindingFlags.GetProperty ||| BindingFlags.InvokeMethod, null, x, args)

let (?<-) x prop v =
    x.GetType().InvokeMember(prop, BindingFlags.SetProperty, null, x, [|v|])

// show methods of a type, e.g. helpt<System.IO.Path>
let helpt<'a> = Seq.iter (printfn "%O") (typeof<'a>.GetMethods())
// show methods of a value,  e.g. help 8
let help (_: 'a) = helpt<'a>

// generic printer
let print o =
    match box o with
    | :? string -> printfn "%O" o
    | _ -> printfn "%A" o


// Unix-like functions

let pwd = Directory.GetCurrentDirectory

let cd = Directory.SetCurrentDirectory

let cat = System.IO.File.ReadAllLines

let ls dir =
    [ yield! Array.map string (Directory.GetFiles(dir, "*"))
      yield! Array.map string (Directory.GetDirectories(dir))]

let rec find dir filter =
    [ yield! Directory.GetFiles(dir, filter)
      for subdir in Directory.GetDirectories(dir) do yield! find subdir filter]

// execute a command
let exec (cmd: string) =
    let cmd = cmd.Split()
    use proc = new System.Diagnostics.Process()
    proc.EnableRaisingEvents <- false
    proc.StartInfo.FileName <- cmd.[0]
    proc.StartInfo.Arguments <- String.concat "" cmd.[1..]
    proc.Start() |> ignore
    proc.WaitForExit()
